为什么写这篇

前一天那台跑 AI 服务的机器连着报错,人在外面,手边只有一部手机。这种时候最清楚:能不能从手机上 SSH 上去、能不能上去就把服务拉起来,决定了你是"十分钟解决"还是"回家再说明天再说"。

手机 SSH 这件事教程很多,但很多坑是这两年才变了的:Termux 在 Google Play 上早就停更、架构包要选对、国产 ROM 会杀后台、以及一个几乎没人写的前提——现在正经服务器都关了密码登录,老教程里的 ssh-copy-id 根本走不通。这篇按本机真实配置和真实输出写,命令都能直接抄。

一、先搞清楚这台机器允许你怎么登录

别先装客户端,先在已有登录通道的机器上看一眼策略,一条命令全出来(不用猜配置文件里哪条被 Include 覆盖了):

sshd -T | grep -E "^(port|passwordauthentication|pubkeyauthentication|permitrootlogin|maxauthtries|clientalive)"

本机(阿里云 ECS,Ubuntu 22.04.5 LTS,OpenSSH_8.9p1)实测返回:

port 22
maxauthtries 6
clientaliveinterval 0
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication no

三行结论:

  1. passwordauthentication no → 只能公钥登录,手机上必须先有私钥,没有"输密码"这条路;
  1. 同理 ssh-copy-id 也走不通(它靠密码认证),公钥只能手动贴;
  1. clientaliveinterval 0 → 服务端不发保活包,移动网络的空闲断连得靠客户端自己解决(见第五节)。

顺便说一句,密码登录关掉之后,公网爆破基本就废了。本机 /var/log/auth.log(当前这份从 10-25 轮转开始)里累计 24 条 Invalid user / Failed password 记录、64 个不同来源 IP,而成功登录的行全是 Accepted publickey,来源只有自己的四个 IP——一条密码登录都没有,因为这条路本来就不存在。fail2ban-client 还没装,不是必须装:禁掉密码认证之后它的边际收益很小。

二、装对 Termux:渠道和架构比手速重要

Termux 是 Android 上的终端模拟器 + 包管理器,不需要 root 就能拿到一整套 Linux 用户态命令(安装位置在 App 自己的数据目录 /data/data/com.termux/files/usr)。两个决定成败的点:

渠道:官方镜像站的说明里写得很直接——"Google Play 上的 Termux 已被弃用,如安装会产生兼容性问题,请通过 GitHub 或 F-Droid 来安装 Termux"(https://mirrors.ustc.edu.cn/help/termux.html)。华为/小米应用商店里那份同理是陈年包,典型症状是 termux-change-repo 提示 command not found、pkg upgrade 把环境搞坏。正确来源二选一:

  • F-Droid 搜 Termux,认准包名 com.termux;
  • 或者 GitHub 的 termux-app Releases 页(https://github.com/termux/termux-app)直接下 APK。

架构:Termux 按 arm64-v8a / armeabi-v7a / x86_64 / x86 出包,现代手机绝大多数是 arm64-v8a(S24 就是)。从 F-Droid 客户端下载它会按 split APK 只给你需要的那一份;手动从网页下就要自己认后缀,选错的表现是装上闪退或提示不兼容。

装完先打开一次主程序让它把目录初始化出来,再去换源装包,顺序反了容易报目录不存在。插件 Termux:API / Termux:Boot / Termux:Widget 是可选的(包名各不同),先装插件不装主程序等于白折腾。

三、三条初始化命令

termux-setup-storage          # 授权访问手机下载/相册目录,弹框要点"允许"
termux-change-repo            # 选 Single mirror → 挑 mirrors.ustc.edu.cn 或 tuna,国内速度差别很大
pkg update && pkg install openssh tmux

termux-change-repo 是 termux-tools 里的命令,如果你连它都找不到,基本就是装成了停更的旧版,卸掉换官方渠道重装(注意别用"覆盖安装"绕过签名冲突,Android 不允许)。

四、生成密钥,只把公钥交出去

在手机上:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "s24"
cat ~/.ssh/id_ed25519.pub

几个细节决定能不能连上、以及安全性:

  • -C "s24" 这个注释一定要起。服务器上的 authorized_keys 是多人多设备共用的,三个月后你不记得哪行是谁。
  • 私钥(id_ed25519,没有 .pub 的那个)永远不要发给任何人、不要贴到任何聊天框。要贴出去的只有 .pub。
  • 口令(passphrase)建议设,但手机上进 Termux 输口令很烦;折中做法是给私钥设强口令 + 手机设锁屏,或者干脆只在手机上留密钥、依赖手机本身的加密存储。
  • Termux 的 ~/.ssh 在 /data/data/com.termux/files/home/.ssh,权限 700,里面文件 600。

把公钥送到服务器。既然密码登录是关的,常见路径有三种:云厂商控制台的 VNC/网页终端、已有的另一台机器上的会话、或者你本来就在服务器上跑着的 AI 助手。贴的时候注意用追加重定向,不要用覆盖:

echo 'ssh-ed25519 AAAA...你的公钥... s24' >> /root/.ssh/authorized_keys
chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys

> 会一把清掉别人所有密钥,这是手机第一次连服务器最容易犯的灾难性错误。

然后手机上:

ssh -i ~/.ssh/id_ed25519 root@你的服务器IP

五、把"断线"和"任务被杀"这两件事一次性解决

手机 SSH 和电脑 SSH 最大的区别不是屏幕小,而是网络在切、系统在杀进程。

客户端保活,~/.ssh/config(Termux 里自己建):

Host myvps
    HostName 你的服务器IP
    User root
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60
    ServerAliveCountMax 3

ServerAliveInterval 60 让客户端每 60 秒发一个探测包,移动网络空闲时 NAT 表项被回收、链路假死这类问题就靠它顶;服务端 clientaliveinterval 0 也不用改,客户端这侧生效就够了。

别让 Android 把 Termux 睡死:下拉通知栏点 Termux 的常驻通知(hold wakelock),或者在会话里执行 termux-wake-lock;再去设置里把 Termux 加入电池优化白名单/允许后台运行(国产 ROM 这一项必做,不然息屏三分钟后一切都白搭)。

最关键的一条:别在没有会话保护的情况下跑长任务。手机上敲一条要跑十分钟的命令,一断线,SIGHUP 就把它带走了。服务器上有 tmux 就行(本机 /usr/bin/tmux 在):

tmux new -A -s ops      # 有 ops 会话就 attach,没有就创建
# 任务照常跑,断线了重连,再敲同一条命令回来

不想用 tmux 的最小写法是重定向加 nohup:

nohup /some/long/task.sh </dev/null >/tmp/task.log 2>&1 &

stdin 断开、输出落盘,断线不影响它活着——这是我在这台机器上给转换器和登录驱动脚本实际用的写法。

六、手机上的 30 秒体检清单

存成服务器上的 ~/bin/ops-check.sh,出门在外 SSH 上去一条命令看完:

#!/bin/bash
date; uptime
free -h | head -2; df -h / | tail -1
systemctl is-active codebuddy-converter 2>/dev/null
# 注意:openclaw 的网关是 admin 用户的 user unit,root 下要用 --user 查
runuser -u admin -- env XDG_RUNTIME_DIR=/run/user/1000 \
  systemctl --user is-active openclaw-gateway 2>/dev/null
ss -ltn | grep -E ":(22|80|443|8787|18789)\b"
curl -s --max-time 5 http://127.0.0.1:8787/health | head -c 200; echo
grep -cE "Failed password|Invalid user" /var/log/auth.log   # 当前日志累计的失败尝试数

本机今天(10-31 17:35)的真实输出:

Sat Oct 31 05:35:20 PM CST 2026
 17:35:20 up 1 day,  1:29,  0 users,  load average: 0.09, 0.10, 0.07
               total        used        free      shared  buff/cache   available
Mem:           3.4Gi       2.5Gi       136Mi       2.0Mi       800Mi       700Mi
/dev/vda3        49G   19G   28G  41% /
active
active
LISTEN 0      511          0.0.0.0:443        0.0.0.0:*
LISTEN 0      128          0.0.0.0:22         0.0.0.0:*
LISTEN 0      511          0.0.0.0:80         0.0.0.0:*
LISTEN 0      511        127.0.0.1:18789      0.0.0.0:*
LISTEN 0      2048       127.0.0.1:8787       0.0.0.0:*
{"status":"ok","platform":"linux","python":"3.10.12","auth_file":"/root/.local/share/..."}
24

七行就够:时间/负载、内存、磁盘、两个服务是不是 active、端口在不在听、转换器认的是哪份凭据、这份日志里累计的失败尝试数。18789 和 8787 只 listen 在 127.0.0.1 也是这条清单要确认的事——它们本来就不该对公网开。

看日志比看监控面板快:先确认端口在听、服务 active、外部能 curl 通,再决定要不要动配置。

七、善后:临时密钥是最容易被忘掉的风险

这台机器的 /root/.ssh/authorized_keys 现在有 6 行,注释分别是:

openclaw-server / qoder-session-20261010 / s24 /
qoder-session-20261020 / qoder-session-20261030 / biaole-deploy-20261030

三个 qoder-session-* 是不同日期里临时助手环境生成的密钥。临时环境会被回收,回收之后私钥就没了——但公钥还大咧咧留在你的服务器上。这件事本身不炸(私钥别人也拿不到),但它说明一类普遍问题:你并不知道自己到底给这台机器开了多少扇门。

维护方式很朴素:

# 看都有谁
awk '{print NR": "$1" ... "$NF}' /root/.ssh/authorized_keys
# 删掉确定不要的那一行(按注释精确删,别整文件覆盖)
sed -i '/qoder-session-20261010/d' /root/.ssh/authorized_keys

手机丢了怎么办?用另一台还能登的机器(或者云控制台 VNC)把 s24 那行删掉,几分钟就封住;这也就是为什么注释名重要——紧急时刻没空比对公钥指纹。

实战踩坑提醒

  • 别从应用商店装 Termux。Play 版停更多年,典型症状是换源命令缺失、pkg upgrade 崩环境。
  • 架构选错就是闪退。现代手机选 arm64-v8a,不确定就看 F-Droid 自动推荐的那份。
  • 密码登录关着的机器,ssh-copy-id 一定失败。别在这上面反复重试,直接走控制台/VNC 手动贴公钥。
  • 贴公钥永远用 >>,不要 >。覆盖写会把服务器上别人的密钥一起清掉。
  • 不加保活、不做电池白名单,手机 SSH 就是个玩具。息屏断连 + 会话被杀,长任务必丢。
  • authorized_keys 每半年通读一遍,注释名是你唯一能看懂哪行是谁的东西。

小结

一条能被手机救回来的服务器,配置上其实就三件事:只允许公钥登录、每台设备一个有注释的密钥、长任务放在 tmux 或 nohup 里。手机上要做的也是三件事:装官方渠道的 Termux、pkg install openssh tmux、给自己配一个 ServerAliveInterval。剩下的都是体力活——比如把服务器状态收敛成一个 ops-check.sh,让在外面时的那 30 秒真的能看出问题。


扫码关注公众号,获取更多 OpenClaw 实操技巧:

公众号二维码